package com.lwei.tallybook.service.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lwei.tallybook.base.domain.Account;
import com.lwei.tallybook.base.domain.AccountBook;
import com.lwei.tallybook.base.domain.Dictionary;
import com.lwei.tallybook.base.domain.rel.RelBookAccount;
import com.lwei.tallybook.service.mapper.AccountBookMapper;
import com.lwei.tallybook.service.service.*;
import com.lwei.tallybook.service.vo.response.BookResponseVO;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author sks.lwei
 */
@Log4j2
@Service
public class AccountBookServiceImpl extends ServiceImpl<AccountBookMapper, AccountBook> implements AccountBookService {

    @Resource
    private DictionaryService dictionaryService;

    @Resource
    private AccountService accountService;


    @Resource
    private RelBookAccountService relBookAccountService;

    /**
     * 账单与账本关联
     */
    @Resource
    private RelBillBookService relBillBookService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public String saveAccountBook(AccountBook accountBook) {
        accountBook.setUuid("ab_"+ IdWorker.getIdStr());
        if(StringUtils.isBlank(accountBook.getConsumeName())){
            Dictionary dictionary = dictionaryService.getDictionaryByKey(accountBook.getConsumeType());
            accountBook.setConsumeName(dictionary.getDictionaryVal());
        }

        if(super.save(accountBook)){
            log.info("accountBook 保存成功");
            String[] consumeParticipator = accountBook.getConsumeParticipator();
            List<RelBookAccount> relBookAccounts = Arrays.stream(consumeParticipator).map(au ->
                    new RelBookAccount(accountBook.getUuid(), au, accountBook.getConsumeTime())).collect(Collectors.toList());
            relBookAccountService.saveBatch(relBookAccounts);
            log.info("accountBook 关联保存成功");
        }
        return accountBook.getUuid();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editAccountBook(AccountBook accountBook) {
        AccountBook book = super.getById(accountBook.getId());
        if(book == null){
            throw new NullPointerException("不存在的账本");
        }
        //修改账本需要修改账户关联
        relBookAccountService.removeByBook(Collections.singletonList(accountBook.getUuid()));
        log.info("账本与账户关系清理完成");
        super.updateById(accountBook);
        log.info("账本修改完毕");
        String[] consumeParticipator = accountBook.getConsumeParticipator();
        List<RelBookAccount> relBookAccounts = Arrays.stream(consumeParticipator).map(au ->
                new RelBookAccount(accountBook.getUuid(), au, accountBook.getConsumeTime())).collect(Collectors.toList());
        relBookAccountService.saveBatch(relBookAccounts);
        log.info("账本与账户关系重新生成");
    }

    /**
     * AccountBook 转 BookResponseVO
     */
    private IPage<BookResponseVO> convert(IPage<AccountBook> pages){
        List<Account> accountList = accountService.listAll();
        List<Dictionary> consumeTypes = dictionaryService.listDictionaryByType("default");

        Map<String, String> accountMap = accountList.stream().collect(Collectors.toMap(Account::getUuid, Account::getUserName));
        Map<String, String> consumeTypesMap = consumeTypes.stream().collect(Collectors.toMap(Dictionary::getDictionaryKey, Dictionary::getDictionaryVal));
        Map<String, Object> param = new HashMap<>(accountMap);
        param.putAll(consumeTypesMap);

        return pages.convert(book -> {
            List<RelBookAccount> relBookAccounts = relBookAccountService.listRelByBookUuid(book.getUuid());
            List<String> joinUuids = relBookAccounts.stream().map(RelBookAccount::getAccountUuid).collect(Collectors.toList());

            return BookResponseVO.getInstance(book, joinUuids, param);
        });
    }

    @Override
    public  IPage<BookResponseVO> listAccountBookByOwner(IPage<AccountBook> page, String accountUuid) {
        QueryWrapper<AccountBook> wrapper = new QueryWrapper<>();
        wrapper.eq("consume_owner", accountUuid);
        wrapper.orderByDesc("create_time");
        IPage<AccountBook> iPage = super.page(page, wrapper);
        return iPage.convert(BookResponseVO::getInstance);
    }

    @Override
    public IPage<BookResponseVO> listAccountBookByJoin(IPage<AccountBook> page, String joinUuid) {
        List<RelBookAccount> relBookAccounts = relBookAccountService.listRelByAccountUuid(joinUuid);
        List<String> bookUuids = relBookAccounts.stream().map(RelBookAccount::getBookUuid).collect(Collectors.toList());
        if(bookUuids.size() == 0){
            return convert(page);
        }
        QueryWrapper<AccountBook> wrapper = new QueryWrapper<>();
        wrapper.in("uuid", bookUuids);
        wrapper.orderByDesc("consume_time");
        return convert(super.page(page, wrapper));
    }

    @Override
    public IPage<BookResponseVO> listBookByBill(IPage<AccountBook> page, String billUuid) {
        List<String> books = relBillBookService.getBookByBill(billUuid);
        if(books.size() == 0){
            return convert(page);
        }
        QueryWrapper<AccountBook> wrapper = new QueryWrapper<>();
        wrapper.in("uuid", books);
        return convert(super.page(page, wrapper));
    }

    @Override
    public IPage<BookResponseVO> listBookByBillWhitJoin(IPage<AccountBook> page, String billUuid, String joinUuid) {
        List<String> books = relBillBookService.getBookByBill(billUuid);
        List<RelBookAccount> relBookAccounts = relBookAccountService.listRelByAccountUuid(joinUuid);
        //筛选出参与其中的book'
        List<String> bookUuids = relBookAccounts.stream().filter(rel -> books.contains(rel.getBookUuid()))
                .map(RelBookAccount::getBookUuid).collect(Collectors.toList());
        if(bookUuids.size() == 0){
            return convert(page);
        }
        QueryWrapper<AccountBook> wrapper = new QueryWrapper<>();
        wrapper.in("uuid", bookUuids);
        return convert(super.page(page, wrapper));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void killAccountBook(List<String> bookUuid) {
        UpdateWrapper<AccountBook> wrapper = new UpdateWrapper<>();
        wrapper.set("close", true);
        wrapper.in("uuid", bookUuid);
        super.update(wrapper);
    }

    @Override
    public void removeAccountBook(List<String> bookUuid) {
        QueryWrapper<AccountBook> wrapper = new QueryWrapper<>();
        wrapper.in("uuid", bookUuid);
        super.remove(wrapper);
        //删除账本与人员的关联
        relBookAccountService.removeByBook(bookUuid);
    }

    @Override
    public AccountBook getAccountBookByUuid(String uuid) {
        QueryWrapper<AccountBook> wrapper = new QueryWrapper<>();
        wrapper.eq("uuid", uuid);
        AccountBook accountBook = super.getOne(wrapper);
        List<RelBookAccount> relBookAccounts = relBookAccountService.listRelByBookUuid(accountBook.getUuid());
        accountBook.setConsumeParticipator(relBookAccounts.stream().map(RelBookAccount::getAccountUuid).toArray(String[]::new));
        return accountBook;
    }

    @Override
    public List<Account> listAccountByBook(String bookUuid) {
        List<RelBookAccount> relBookAccounts = relBookAccountService.listRelByBookUuid(bookUuid);
        List<String> uuids = relBookAccounts.stream().map(RelBookAccount::getAccountUuid).collect(Collectors.toList());
        return accountService.listInUuids(uuids);
    }

    @Override
    public List<AccountBook> listBookBetweenTime(Date begin, Date end) {
        QueryWrapper<AccountBook> wrapper = new QueryWrapper<>();
        wrapper.between("consume_time", begin, end);
        wrapper.eq("close", false);
        return super.list(wrapper);
    }

    @Override
    public List<AccountBook> listBookBeforeTime(Date end) {
        QueryWrapper<AccountBook> wrapper = new QueryWrapper<>();
        wrapper.le("consume_time", end);
        wrapper.eq("close", false);
        return super.list(wrapper);
    }
}
